Sissejuhatus

Selles selles ja järgmises praktikumis tutuvume paketiga ggplot2 mis implementeerib graafikute grammatika R-s. Selle praktikumi eesmärk on selgeks saada üldisem ggplot2 süntaks ning järgmises harjutame erinevat tüüpi andmetele mõistlike visualisatsioonide leidmisele. Enne kui läheme edasi tasub ggplot2 sisse lugeda.

library(ggplot2)

Lisaks lugege sisse andmestik linnad.RData käsuga load.

load("linnad.RData", verbose = T)
Loading objects:
  linnad
linnad

Graafiku loomine ggplot2-ga

ggplot2 puhul ei kasutata tavaliselt ühte suurt funktsiooni mis kogu pildi korraga valmis teeb. Selle asemel on ports funktsioone, millega erinevaid kihte saab defineerida ja pildi väljanägemist mõjutada. Esmane funktsioon millega pildi joonistamist alustada on ggplot. Selle argumentideks on andmestik ja soovitavalt ka seosed andmete ja graafiliste elementide vahel. Need seosed tuleb panna funktsiooni aes sisse, mis annb ggplot2-le teada, et nende tunnuste väärtused tuleb ära teisendada. Siin defineeritud tunnuste ja graafiliste esitusviiside soesed kanduvad edasi ka järgnevalt defineeritud konkreetsetesse kihtidesse.

ggplot(linnad, aes(x = per_capita_inc, y = unemployment_rate))

Selleks et vastavaid operatsioone teostada, need liidetakse tehtega + olemasolevale pildi objektile. Kihte saab pildile lisada käskudega geom_*. Lisame pildile punktide kihi käsuga geom_point.

ggplot(linnad, aes(x = per_capita_inc, y = unemployment_rate)) + 
  geom_point() +
  theme_bw()

Punktidel on veel rida graafilisi atribuute, mida me saame ära kasutada andmete edasi andmiseks. Näiteks color, size ja shape on vastavalt värv, suurus ja kuju.

ggplot(linnad, aes(x = per_capita_inc, y = unemployment_rate, color = bachelor)) + 
  geom_point()

ggplot(linnad, aes(x = per_capita_inc, y = unemployment_rate, color = bachelor, shape = State)) + 
  geom_point()

Ülesanded

  • Joonista välja hajuvusdiagramm keskkooli ja ülikooli lõpetanute protsendi vahel (tunnused high_scl ja bachelor). Kas tunnuste vahel on seos?

  • Kujutada graafikul ka osariiki (tunnus State). Proovi selleks erinevaid variante. Kas haridustasemed on osariikide kaupa erinevad?

  • Kujuta graafikul ka maakonna rahvaarvu (tunnus pop_estimate), kasutades näiteks punkti suurust.


ggplot(linnad, aes(x = high_scl, y = bachelor, color = State, size=pop_estimate)) + 
  geom_point()

Geomeetrilised esitused

ggplot2 defineerib loomulikult märgatavalt rohkem geomeetrilis esitusi, kui ainult punktid. Järgnevalt on toodud mõned tavalisemad.

Täielik ülevaade olemasolevatest vahenditest on lehel https://ggplot2.tidyverse.org/reference/index.html. Mõne uue geom-i kasutamisel tasub uurida ka abilehti, kus Aesthetics sektsioonis on kirjas millised parameetrid peab vastavale kujundile ette andma.

?geom_bar()
ggplot(linnad, aes(x = perc_poverty)) +
  geom_histogram()

ggplot(linnad, aes(x = Birth_factor, y = perc_poverty)) +
  geom_boxplot()

ggplot(linnad, aes(x = Birth_factor)) +
  geom_bar()

Ülesanded

  • Joonista välja tunnuse bachelor histogramm .  

  • Lisa eelmisele pildile värviga ka tunnus State. (Tulpdiagrammi puhul saab värvi määrata parameetriga fill)

  • Uuri tunnuse high_scl jaotust erinevates osariikides kasutades karpdiagrammi.

ggplot(linnad, aes(x = bachelor, fill=State)) +
  geom_histogram()

ggplot(linnad, aes(x = high_scl, y = State)) +
  geom_boxplot()

Kihtide täiendamine

Graafilised atribuudid

Andmete seoseid graafiliste elementidega saab lisada ka otse konkreetsele kihile, kasutades funktsioni aes() kihi välja kutsumisel. Kui me aga tahame mõnda atribuuti muuta andmetest sõltumatult, siis me saame sellele väärtuse anda funktsioonist aes() väljaspool.

ggplot(linnad, aes(x = per_capita_inc, y = unemployment_rate)) + 
  geom_point(aes(color = bachelor))

ggplot(linnad, aes(x = per_capita_inc, y = unemployment_rate)) + 
  geom_point(color = "gold")

Statistilised teisendused

Graafikute grammatikas on kihi definitsiooni üheks osaks ka statistiline teisendus. Histogrammi jaoks on vaja jagada andmed võrdse pikkkusega vahemikeks ja siis igas vahemikus lugeda kokku sinna langevad vaatlused. Karpdiagrammil on vaja leida mediaan ja kvantiilid. Kui karpdiagrammi ilma teisenduseta joonistada ei saa, siis tulpdiagrammi puhul on on vahest mõistlik lasta automaatselt vaatluste arvud kokku lugeda. Teinekord jälle tahaks tulba kõrguse ise ette anda. Selleks on kõigil geom_* käskudel stat parameeter. geom_bar abilehelt on näha, et vaikimis väärtus sellel parameetril on "count". See tähendab, et kui anname ette diskreetse tunnuse loetakse kokku iga väärtusega vaatlused ja joonistatakse need.

ggplot(linnad, aes(x = State)) +
  geom_bar()

Kui tahame aga tulba kõrgused ette anda saame errori, sest geom_bar soovib y väärtust ise arvutada. Selleks peame muutma statistilise teisenduse nime ja meile sobivaks teisenduseks on siinkohal "identity".

ggplot(linnad, aes(x = County, y = pop_estimate)) +
  geom_bar()
Error in `geom_bar()`:
! Problem while computing stat.
ℹ Error occurred in the 1st layer.
Caused by error in `setup_params()`:
! `stat_count()` must only have an x or y aesthetic.
Run `]8;;x-r-run:rlang::last_trace()rlang::last_trace()]8;;` to see where the error occurred.

ggplot(linnad, aes(x = County, y = pop_estimate)) +
  geom_bar(stat = "identity")

Positsioon

Üks kihi komponent on ka positsioon, mis kontrollib teatud graafiliste elementide paigutus. Enamasti kasutatakse seda värviliste tulpdiagrammide joonistamisel. Selle parameetri erinevatel väärtustel on järgmised tagajärjed:

  • "dodge" - paneb  erinevat värvi tulbad kõrvuti

  • "stack"- paneb erinevat värvi tulbad üksteise otsa

  • "fill" - paneb tulbad üksteise otsa ja teisendab ühekõrguseks (kasulik kui uurida osakaalusid)

ggplot(linnad, aes(x = Birth_factor, fill = Poverty_factor)) +
  geom_bar(position = "dodge")

ggplot(linnad, aes(x = Birth_factor, fill = Poverty_factor)) +
  geom_bar(position = "stack")

ggplot(linnad, aes(x = Birth_factor, fill = Poverty_factor)) +
  geom_bar(position = "fill")

Hajuvusdiagrammi puhul saab kasutada väärtust "jitter", mis lisab andmetele natuke müra, et punktid ei oleks nii üksteise otsas. 

ggplot(linnad, aes(x = Birth_factor, y = perc_poverty)) +
  geom_point()

ggplot(linnad, aes(x = Birth_factor, y = perc_poverty)) +
  geom_point(position = "jitter")

Mitme kihi lisamine

Kihte võib lisada mitu. Vaikimisi võetakse andmed ja seosed andmete ning graafiliste elementide vahel ülalolevast ggplot käsust, kuid loomulikutl võib uusi kihte täiendada nii nagu ülal kirjeldatud.

ggplot(linnad, aes(x = per_capita_inc, y = unemployment_rate, color = bachelor)) + 
  geom_point() + 
  geom_smooth()

Kihtidele võib ette anda ka uusi andmeid parameetriga data.

library(tidyverse)

ggplot(linnad, aes(x = per_capita_inc, y = unemployment_rate, color = bachelor)) + 
  geom_point() +
  geom_text(aes(label = County), color = "red", hjust = 0, data = linnad %>% filter(pop_estimate > 3000000))

Ülesanded

  • Joonista tulpdiagramme mis uurivad tunnuse Birth_factor jaotust osariikide kaupa. Milline position väärtus annab kõige mõistlikuma graafiku?

  • Joonista hajuvusdiagramm keskkooli ja ülikooli lõpetanute protsendi vahel (high_scl ja bachelor).

  • Lisa neile maakondadele nimed, kus keskkooli haridusega inimeste osakaal on alla 50-e.

  • Lisa graafikule vertikaalne joon kohal x = 50. (vihje: kasuta käsku geom_vline)


ggplot(linnad, aes(x = Birth_factor, fill = State)) +
  geom_bar(position = "dodge")


ggplot(linnad, aes(x = Birth_factor, fill = State)) +
  geom_bar(position = "stack")


ggplot(linnad, aes(x = Birth_factor, fill = State)) +
  geom_bar(position = "fill")

ggplot(linnad, aes(y = high_scl , x= bachelor)) + 
  geom_point() +
  geom_text(aes(label = County), color = "red", hjust = 0, data = linnad %>% filter(high_scl < 50)) +
  geom_hline(yintercept = 50)

Graafiku jagamine tahkudeks

Tihti on kasulik ühe suure pildi asemel joonistada palju väikeseid saranse sisuga pilte, kusjuures objektide jagamine akendese on toimub mingi tunnuse väärtuste põhjal. Seda saab saaavutada käskudega facet_grid ja facet_wrap, neist esimene paigutab pilte ridadesse ja veergudesse graafikute maatriksis ning teine proovib paigutada ühe muutuja põhjal jatatud graafikud võimalikult kompaktselt.

facet_grid puhul tuleb ette anda parameeter kujul <ridadeks jagav muutuja> ~ <veergudeks jagav muutuja>, kui üks neist ära jätta, siis tuleb selle asemele kirjutada punkt.

ggplot(linnad, aes(x = per_capita_inc, y = unemployment_rate, color = bachelor)) + 
  geom_point() +
  facet_grid(. ~ State)

ggplot(linnad, aes(x = per_capita_inc, y = unemployment_rate, color = bachelor)) + 
  geom_point() +
  facet_grid(Birth_factor ~ State)

facet_wrap puhul antakse parameeter ette kujul ~ <muutuja nimi>.

ggplot(linnad, aes(x = per_capita_inc, y = unemployment_rate, color = bachelor)) + 
  geom_point() +
  facet_wrap(~ State)

Skaleerimine

Et muuta viisi, kuidas ggplot2 muutujaid graafiliseteks parameetriteks teisendab saab kasutada käske scale_<graafiline parameeter>_<skaala nimi>. Näiteks funktsiooni scale_x_continuous saab kasutada x-teljel asuva pideva tunnuse skaleerimiseks ja scale_colour_grey on värvi skaleerimiseks mustvalgel skaalal. Kõiki relevantseid skaleerimisfunktsioone on võimalik näha ggplot2 kodulehel https://ggplot2.tidyverse.org/reference/index.html#section-scales.

Skaleerimisfunktsiooni rakendamiseks tuleb ta liita graafikule. 

ggplot(linnad, aes(x = per_capita_inc, y = unemployment_rate, color = bachelor)) + 
  geom_point()

ggplot(linnad, aes(x = per_capita_inc, y = unemployment_rate, color = bachelor)) + 
  geom_point() +
  scale_x_continuous(trans = "log10")

Iga erineva parameetri skaleerimiseks saab graafikule liita uue funktsiooni. Konkreetsete funktsioonide parameetrid sõltuvad palju tunnuse tüübist ja graafilise parameetri omadustest, kuid sellegipoolest on olemas mõned argumendid, mida kõik skaleerimisfunktsioonid tunnistavad.

ggplot(linnad, aes(x = per_capita_inc, y = unemployment_rate, color = State)) + 
  geom_point() +
  scale_x_continuous(name = "Per capita income", breaks = c(10000, 40000))

ggplot(linnad, aes(x = per_capita_inc, y = unemployment_rate, color = State)) + 
  geom_point() +
  scale_x_continuous(name = "Per capita income", breaks = c(10000, 40000), labels = c("Small", "Large"))

Pidevate tunnuste skaleerimine

Pidevate muutujate skaleerimiseks kasutatavatel funktsioonidel (näit. scale_*_continuous, scale_*_gradient, …) on mõned spetsiifilised argumendid.

  • trans - saame anda ette transformatsiooni nime, mida rakendada väärtustele enne pildile panemist. Mõned võimalikud väärtused: "exp", "log2", "log10", "pow10", "sqrt". … .

  • limits - kahe elemendiline vektor, mis määrab ära telje vähima ja suurima punkti.

ggplot(linnad, aes(x = per_capita_inc, y = unemployment_rate, colour = State)) +
  geom_point() + 
  scale_x_continuous(trans = "log10")

ggplot(linnad, aes(x = per_capita_inc, y = unemployment_rate, colour = State)) +
  geom_point() + 
  scale_x_continuous(limits = c(0, 100000))

Diskreetsete muutujate skaleerimine

Diskreetsete muutjate skaleerimiseks kasutatakse funktsiooni scale_*_discrete. Parameeter limits töötab diskreetete muutujate puhul teisiti, kui pideval juhul. Kuna me ei saa ette anda väärtuste vahemiku otspunkte, siis peame ette andma kõik väärtused, mida me tahame kujutada, koos järjekorraga.

ggplot(linnad, aes(x = State, y = unemployment_rate)) +
  geom_boxplot() + 
  scale_x_discrete(limits = c("Texas", "Maryland"))

Värvide skaleerimine

Värvide valik on graafikute puhul väga oluline. Õigesti valitud värvidega on võimalik tuua selgemini välja oma sõnumit, muuta graafikut loetavamaks ja ka visuaalselt meeldivamaks ning professionaalsemaks.  Asjakohane värviskaala sõltub jällegi palju tunnusest mida tahame kujutada.  Väga üldiselt võib värviskaalad jagada kolmeks: 

  • gradient - kasutatakse pidevate tunnuste kujutamisel, kõige väiksem väärtus ja suurem väärtus vastavad mingitele värvidele ning vahepealsed väärtused siis nende kahe värvi segudele;

  • lahknev gradient - kui pideval tunnusel on mingi selge nullpunkt, siis saab seda visuaalselt kujutada kolmeastmelise gradiendiga, kus kaks värvi on reserveeritud ekstreemsetele väärtustele ja üks siis nn nullpunkti jaoks. Näiteks kui kujutame temperatuure, on loomulik kujutada positiivseid väärtusi erineva tugevusega punastega ja negatiivseid samamoodi sinise varjunditega;

  • kvalitatiivne - iga väärtuse kujutamiseks kasutatakse võimalikult erinevat värvitooni, samas on oluline silmas pidada, et heleduselt ja värvi tugevuselt oleksid kõik toonid sarnased, sest muidu hakkavad teatud väärtused teiste üle domineerima;

Kõiki neid variante on ggplotiga suhteliselt lihtne saavutada. Gradiendi kontrollimiseks saab kasutada funktsiooni scale_*_gradient. Seal töötavad kõike eelpool tutvustatud pidevate skaleerimisfunktsioonide argumendid ning lisaks on kaks parameetrit low ja high, mis määravad ära siis gradiendi alguse ja lõpu värvi.

ggplot(linnad, aes(x = per_capita_inc, y = unemployment_rate, colour = bachelor)) +
  geom_point()

ggplot(linnad, aes(x = per_capita_inc, y = unemployment_rate, colour = bachelor)) +
  geom_point() + 
  scale_colour_gradient(name = "Perc. bachelor", low = "yellow", high = "red")

ggplot(linnad, aes(x = per_capita_inc, y = unemployment_rate, colour = bachelor)) +
  geom_point() + 
  scale_colour_gradient(name = "Perc. bachelor", low = "yellow", high = "red", breaks = c(10, 30, 50))

Lahknevat ja veel keerulisemaid gradiente saab funktsiooniga scale_*_gradientn, mille parameetrile colors saab ette anda vektori värvidega mille vahele ta siis uued värvid interpoleerib.

ggplot(linnad, aes(x = per_capita_inc, y = unemployment_rate, colour = bachelor - mean(bachelor))) +
  geom_point()

ggplot(linnad, aes(x = per_capita_inc, y = unemployment_rate, colour = bachelor - mean(bachelor))) +
  geom_point() + 
  scale_colour_gradientn(name = "Perc. bachelor", colours = c("yellow", "white", "red"))

ggplot(linnad, aes(x = per_capita_inc, y = unemployment_rate, colour = bachelor - mean(bachelor))) +
  geom_point() + 
  scale_colour_gradientn(name = "Perc. bachelor", colours = c("yellow", "white", "red"), limits = c(-40, 40))

Diskreetsete/kvalitatiivsete väärtuste puhul kasutatakse vaikimisi funktsiooni scale_*_hue, mis valib HCL värviskaalal, parameetri hue võimalikult erinevad väärtused jättes värvi tugevuse ja heleduse konstantseks. Nii saame võimalikult erinevad värvid, mis samal ajal peaks olema sarnase intensiivsusega. 

Kõige kasulikum funktsioon on vast scale_*_brewer, kus saab valida algselt maakaartide värvimiseks aendatud värvipalette (http://colorbrewer2.org/). Seal on kaks parameetrit type ja palette. Argumendi type võimalilud väärtused on “seq”, “div” ja “qual”, mis siis tähistavad erinevaid paleti tüüpe. Palett väärtuseks saab anda paleti numbri. 

Funktsioon scale_*_brewer eeldab diskreetseid andmeid.

ggplot(linnad, aes(x = per_capita_inc, y = unemployment_rate, colour = State)) +
  geom_point() + 
  scale_colour_brewer(type = "qual", palette = 6)

ggplot(linnad, aes(x = per_capita_inc, y = unemployment_rate, colour = State)) +
  geom_point() + 
  scale_colour_brewer(type = "qual", palette = 7)

ggplot(linnad, aes(x = per_capita_inc, y = unemployment_rate, colour = birth_class)) +
  geom_point() + 
  scale_colour_brewer(type = "seq", palette = 1)

ggplot(linnad, aes(x = per_capita_inc, y = unemployment_rate, colour = birth_class)) +
  geom_point() + 
  scale_colour_brewer(type = "seq", palette = 2)

ggplot(linnad, aes(x = per_capita_inc, y = unemployment_rate, colour = birth_class)) +
  geom_point() +
  scale_colour_brewer(type = "div", palette = 1)

ggplot(linnad, aes(x = per_capita_inc, y = unemployment_rate, colour = birth_class)) +
  geom_point() +
  scale_colour_brewer(type = "div", palette = 3) 

Populaarne on ka viridis palett (https://bids.github.io/colormap/), mis algselt on arendatud matplotlib paketile pythonis. Selle eelis on, et gradiendid liiguvad läbi mitmete värvitoonide ning nii muutuvad väärtused paremini eristuvaks. Sellegipoolest sobivad need gradiendid ka värvipimedatele ja mustvalgeks trükkimiseks.

Skaala sobib nii diskreetsete kui pidevate tunnuste jaoks, skaala funktsioonid on siis vastavalt scale_*_viridis_d ja scale_*_viridis_c. Paleti versioonideks on: "magma", "inferno" , "plasma" , "viridis" , "cividis" , "rocket" , "mako" ja "turbo" , või siis vastavalt suured tähed "A" - "H" mille saab ette anda option parameetrile.

ggplot(linnad, aes(x = per_capita_inc, y = unemployment_rate, colour = bachelor - mean(bachelor))) +
  geom_point() + 
  scale_colour_viridis_c(name = "Perc. bachelor")

ggplot(linnad, aes(x = per_capita_inc, y = unemployment_rate, colour = bachelor - mean(bachelor))) +
  geom_point() + 
  scale_colour_viridis_c(name = "Perc. bachelor", option = "inferno")

ggplot(linnad, aes(x = per_capita_inc, y = unemployment_rate, colour = State)) +
  geom_point() + 
  scale_colour_viridis_d(option = "cividis")

Ülesanded

  • Jätkates eelmistes ülesannete alustatud diagrammi tunnuste bachelor ja high_scl vahel. Pane vastava telje nimeks “Higher education percentage”; muuda telje ulatust vahemikku (0, 100); muuda silte ja nende paigutust nii, et need oleksid kujul 0%, 25%, 50%, 75% 100%.
  • Lisa pildile värviga ka tunnus income_class nii, et näidatakse ainult punkte kus sissetulek on kas väga kõrge (Very High) või väga madal (Very Low).

  • Proovi erinevaid värviskaalasid tunnusele income_class. Milline sobib kõige paremini ja milline ei sobi üldse?

  • Mis värviskaalat on kasutatud alloleval järgneval pildil?


ggplot(linnad, aes(x = bachelor , y = high_scl, colour = income_class)) +
  geom_point() + 
  scale_x_continuous(name = "Higher education percentage", limits = c(0, 100), labels = c("0%", "25%", "50%", "75%", "100%")) +
  scale_colour_brewer(type = "qual", palette = 3) 

  #scale_colour_viridis_d(name = "Perc. bachelor")

LS0tDQp0aXRsZTogIlByYWt0aWt1bSA3IC0gZ2dwbG90MiBhbHVzZWQiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQojIyBTaXNzZWp1aGF0dXMNCg0KU2VsbGVzIHNlbGxlcyBqYSBqw6RyZ21pc2VzIHByYWt0aWt1bWlzIHR1dHV2dW1lIHBha2V0aWdhIGdncGxvdDIgbWlzIGltcGxlbWVudGVlcmliIGdyYWFmaWt1dGUgZ3JhbW1hdGlrYSBSLXMuIFNlbGxlIHByYWt0aWt1bWkgZWVzbcOkcmsgb24gc2VsZ2VrcyBzYWFkYSDDvGxkaXNlbSBnZ3Bsb3QyIHPDvG50YWtzIG5pbmcgasOkcmdtaXNlcyBoYXJqdXRhbWUgZXJpbmV2YXQgdMO8w7xwaSBhbmRtZXRlbGUgbcO1aXN0bGlrZSB2aXN1YWxpc2F0c2lvb25pZGUgbGVpZG1pc2VsZS4gRW5uZSBrdWkgbMOkaGVtZSBlZGFzaSB0YXN1YiBnZ3Bsb3QyIHNpc3NlIGx1Z2VkYS4NCg0KYGBge3J9DQpsaWJyYXJ5KGdncGxvdDIpDQpgYGANCg0KTGlzYWtzIGx1Z2VnZSBzaXNzZSBhbmRtZXN0aWsgbGlubmFkLlJEYXRhIGvDpHN1Z2EgYGxvYWRgLg0KDQpgYGB7cn0NCmxvYWQoImxpbm5hZC5SRGF0YSIsIHZlcmJvc2UgPSBUKQ0KbGlubmFkDQpgYGANCg0KIyMgR3JhYWZpa3UgbG9vbWluZSBnZ3Bsb3QyLWdhDQoNCmBnZ3Bsb3QyYCBwdWh1bCBlaSBrYXN1dGF0YSB0YXZhbGlzZWx0IMO8aHRlIHN1dXJ0IGZ1bmt0c2lvb25pIG1pcyBrb2d1IHBpbGRpIGtvcnJhZ2EgdmFsbWlzIHRlZWIuIFNlbGxlIGFzZW1lbCBvbiBwb3J0cyBmdW5rdHNpb29uZSwgbWlsbGVnYSBlcmluZXZhaWQga2lodGUgc2FhYiBkZWZpbmVlcmlkYSBqYSBwaWxkaSB2w6RsamFuw6RnZW1pc3QgbcO1anV0YWRhLiBFc21hbmUgZnVua3RzaW9vbiBtaWxsZWdhIHBpbGRpIGpvb25pc3RhbWlzdCBhbHVzdGFkYSBvbiBgZ2dwbG90YC4gU2VsbGUgYXJndW1lbnRpZGVrcyBvbiBhbmRtZXN0aWsgamEgc29vdml0YXZhbHQga2Egc2Vvc2VkIGFuZG1ldGUgamEgZ3JhYWZpbGlzdGUgZWxlbWVudGlkZSB2YWhlbC4gTmVlZCBzZW9zZWQgdHVsZWIgcGFubmEgZnVua3RzaW9vbmkgYGFlc2Agc2lzc2UsIG1pcyBhbm5iIGBnZ3Bsb3QyYC1sZSB0ZWFkYSwgZXQgbmVuZGUgdHVubnVzdGUgdsOkw6RydHVzZWQgdHVsZWIgw6RyYSB0ZWlzZW5kYWRhLiBTaWluIGRlZmluZWVyaXR1ZCB0dW5udXN0ZSBqYSBncmFhZmlsaXN0ZSBlc2l0dXN2aWlzaWRlIHNvZXNlZCBrYW5kdXZhZCBlZGFzaSBrYSBqw6RyZ25ldmFsdCBkZWZpbmVlcml0dWQga29ua3JlZXRzZXRlc3NlIGtpaHRpZGVzc2UuDQoNCmBgYHtyfQ0KZ2dwbG90KGxpbm5hZCwgYWVzKHggPSBwZXJfY2FwaXRhX2luYywgeSA9IHVuZW1wbG95bWVudF9yYXRlKSkNCmBgYA0KDQpTZWxsZWtzIGV0IHZhc3RhdmFpZCBvcGVyYXRzaW9vbmUgdGVvc3RhZGEsIG5lZWQgbGlpZGV0YWtzZSB0ZWh0ZWdhIGArYCBvbGVtYXNvbGV2YWxlIHBpbGRpIG9iamVrdGlsZS4gS2lodGUgc2FhYiBwaWxkaWxlIGxpc2FkYSBrw6Rza3VkZWdhIGBnZW9tXypgLiBMaXNhbWUgcGlsZGlsZSBwdW5rdGlkZSBraWhpIGvDpHN1Z2EgYGdlb21fcG9pbnRgLg0KDQpgYGB7cn0NCmdncGxvdChsaW5uYWQsIGFlcyh4ID0gcGVyX2NhcGl0YV9pbmMsIHkgPSB1bmVtcGxveW1lbnRfcmF0ZSkpICsgDQogIGdlb21fcG9pbnQoKSArDQogIHRoZW1lX2J3KCkNCmBgYA0KDQpQdW5rdGlkZWwgb24gdmVlbCByaWRhIGdyYWFmaWxpc2kgYXRyaWJ1dXRlLCBtaWRhIG1lIHNhYW1lIMOkcmEga2FzdXRhZGEgYW5kbWV0ZSBlZGFzaSBhbmRtaXNla3MuIE7DpGl0ZWtzIGBjb2xvcmAsIGBzaXplYCBqYSBgc2hhcGVgIG9uIHZhc3RhdmFsdCB2w6Rydiwgc3V1cnVzIGphIGt1anUuDQoNCmBgYHtyfQ0KZ2dwbG90KGxpbm5hZCwgYWVzKHggPSBwZXJfY2FwaXRhX2luYywgeSA9IHVuZW1wbG95bWVudF9yYXRlLCBjb2xvciA9IGJhY2hlbG9yKSkgKyANCiAgZ2VvbV9wb2ludCgpDQpgYGANCg0KYGBge3J9DQpnZ3Bsb3QobGlubmFkLCBhZXMoeCA9IHBlcl9jYXBpdGFfaW5jLCB5ID0gdW5lbXBsb3ltZW50X3JhdGUsIGNvbG9yID0gYmFjaGVsb3IsIHNoYXBlID0gU3RhdGUpKSArIA0KICBnZW9tX3BvaW50KCkNCmBgYA0KDQojIyMjIMOcbGVzYW5kZWQNCg0KLSAgIEpvb25pc3RhIHbDpGxqYSBoYWp1dnVzZGlhZ3JhbW0ga2Vza2tvb2xpIGphIMO8bGlrb29saSBsw7VwZXRhbnV0ZSBwcm90c2VuZGkgdmFoZWwgKHR1bm51c2VkICpoaWdoX3NjbCogamEgKmJhY2hlbG9yKikuIEthcyB0dW5udXN0ZSB2YWhlbCBvbiBzZW9zPw0KDQotICAgS3VqdXRhZGEgZ3JhYWZpa3VsIGthIG9zYXJpaWtpICh0dW5udXMgKlN0YXRlKikuIFByb292aSBzZWxsZWtzIGVyaW5ldmFpZCB2YXJpYW50ZS4gS2FzIGhhcmlkdXN0YXNlbWVkIG9uIG9zYXJpaWtpZGUga2F1cGEgZXJpbmV2YWQ/DQoNCi0gICBLdWp1dGEgZ3JhYWZpa3VsIGthIG1hYWtvbm5hIHJhaHZhYXJ2dSAodHVubnVzICpwb3BfZXN0aW1hdGUqKSwga2FzdXRhZGVzIG7DpGl0ZWtzIHB1bmt0aSBzdXVydXN0Lg0KDQpgYGB7cn0NCg0KZ2dwbG90KGxpbm5hZCwgYWVzKHggPSBoaWdoX3NjbCwgeSA9IGJhY2hlbG9yLCBjb2xvciA9IFN0YXRlLCBzaXplPXBvcF9lc3RpbWF0ZSkpICsgDQogIGdlb21fcG9pbnQoKQ0KDQpgYGANCg0KIyMgR2VvbWVldHJpbGlzZWQgZXNpdHVzZWQNCg0KYGdncGxvdDJgIGRlZmluZWVyaWIgbG9vbXVsaWt1bHQgbcOkcmdhdGF2YWx0IHJvaGtlbSBnZW9tZWV0cmlsaXMgZXNpdHVzaSwga3VpIGFpbnVsdCBwdW5rdGlkLiBKw6RyZ25ldmFsdCBvbiB0b29kdWQgbcO1bmVkIHRhdmFsaXNlbWFkLg0KDQotICAgUHVua3RpZDogYGdlb21fcG9pbnRgDQoNCi0gICBKb29uOiBgZ2VvbV9saW5lYA0KDQotICAgVHVscDogYGdlb21fYmFyYA0KDQotICAgSGlzdG9ncmFtbTogYGdlb21faGlzdG9ncmFtYA0KDQotICAgS2FycGRpYWdyYW1tOiBgZ2VvbV9ib3hwbG90YA0KDQotICAgVGVrc3Q6IGBnZW9tX3RleHRgDQoNClTDpGllbGlrIMO8bGV2YWFkZSBvbGVtYXNvbGV2YXRlc3QgdmFoZW5kaXRlc3Qgb24gbGVoZWwgPGh0dHBzOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS9pbmRleC5odG1sPi4gTcO1bmUgdXVlIGdlb20taSBrYXN1dGFtaXNlbCB0YXN1YiB1dXJpZGEga2EgYWJpbGVodGksIGt1cyAqKkFlc3RoZXRpY3MqKiBzZWt0c2lvb25pcyBvbiBraXJqYXMgbWlsbGlzZWQgcGFyYW1lZXRyaWQgcGVhYiB2YXN0YXZhbGUga3VqdW5kaWxlIGV0dGUgYW5kbWEuDQoNCmBgYHtyfQ0KP2dlb21fYmFyKCkNCmBgYA0KDQpgYGB7cn0NCmdncGxvdChsaW5uYWQsIGFlcyh4ID0gcGVyY19wb3ZlcnR5KSkgKw0KICBnZW9tX2hpc3RvZ3JhbSgpDQpgYGANCg0KYGBge3J9DQpnZ3Bsb3QobGlubmFkLCBhZXMoeCA9IEJpcnRoX2ZhY3RvciwgeSA9IHBlcmNfcG92ZXJ0eSkpICsNCiAgZ2VvbV9ib3hwbG90KCkNCmBgYA0KDQpgYGB7cn0NCmdncGxvdChsaW5uYWQsIGFlcyh4ID0gQmlydGhfZmFjdG9yKSkgKw0KICBnZW9tX2JhcigpDQpgYGANCg0KIyMjIyDDnGxlc2FuZGVkDQoNCi0gICBKb29uaXN0YSB2w6RsamEgdHVubnVzZSAqYmFjaGVsb3IqIGhpc3RvZ3JhbW0gLiDCoA0KDQotICAgTGlzYSBlZWxtaXNlbGUgcGlsZGlsZSB2w6RydmlnYSBrYSB0dW5udXMgKlN0YXRlKi4gKFR1bHBkaWFncmFtbWkgcHVodWwgc2FhYiB2w6RydmkgbcOkw6RyYXRhIHBhcmFtZWV0cmlnYSBgZmlsbGApDQoNCi0gICBVdXJpIHR1bm51c2UgKmhpZ2hfc2NsKiBqYW90dXN0IGVyaW5ldmF0ZXMgb3Nhcmlpa2lkZXMga2FzdXRhZGVzIGthcnBkaWFncmFtbWkuDQoNCmBgYHtyfQ0KZ2dwbG90KGxpbm5hZCwgYWVzKHggPSBiYWNoZWxvciwgZmlsbD1TdGF0ZSkpICsNCiAgZ2VvbV9oaXN0b2dyYW0oKQ0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90KGxpbm5hZCwgYWVzKHggPSBoaWdoX3NjbCwgeSA9IFN0YXRlKSkgKw0KICBnZW9tX2JveHBsb3QoKQ0KYGBgDQoNCiMjIEtpaHRpZGUgdMOkaWVuZGFtaW5lDQoNCiMjIyBHcmFhZmlsaXNlZCBhdHJpYnV1ZGlkDQoNCkFuZG1ldGUgc2Vvc2VpZCBncmFhZmlsaXN0ZSBlbGVtZW50aWRlZ2Egc2FhYiBsaXNhZGEga2Egb3RzZSBrb25rcmVldHNlbGUga2loaWxlLCBrYXN1dGFkZXMgZnVua3RzaW9uaSBhZXMoKSBraWhpIHbDpGxqYSBrdXRzdW1pc2VsLiBLdWkgbWUgYWdhIHRhaGFtZSBtw7VuZGEgYXRyaWJ1dXRpIG11dXRhIGFuZG1ldGVzdCBzw7VsdHVtYXR1bHQsIHNpaXMgbWUgc2FhbWUgc2VsbGVsZSB2w6TDpHJ0dXNlIGFuZGEgZnVua3RzaW9vbmlzdCBhZXMoKSB2w6RsamFzcG9vbC4NCg0KYGBge3J9DQpnZ3Bsb3QobGlubmFkLCBhZXMoeCA9IHBlcl9jYXBpdGFfaW5jLCB5ID0gdW5lbXBsb3ltZW50X3JhdGUpKSArIA0KICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IGJhY2hlbG9yKSkNCmBgYA0KDQpgYGB7cn0NCmdncGxvdChsaW5uYWQsIGFlcyh4ID0gcGVyX2NhcGl0YV9pbmMsIHkgPSB1bmVtcGxveW1lbnRfcmF0ZSkpICsgDQogIGdlb21fcG9pbnQoY29sb3IgPSAiZ29sZCIpDQpgYGANCg0KIyMjIFN0YXRpc3RpbGlzZWQgdGVpc2VuZHVzZWQNCg0KR3JhYWZpa3V0ZSBncmFtbWF0aWthcyBvbiBraWhpIGRlZmluaXRzaW9vbmkgw7xoZWtzIG9zYWtzIGthIHN0YXRpc3RpbGluZSB0ZWlzZW5kdXMuIEhpc3RvZ3JhbW1pIGphb2tzIG9uIHZhamEgamFnYWRhIGFuZG1lZCB2w7VyZHNlIHBpa2trdXNlZ2EgdmFoZW1pa2VrcyBqYSBzaWlzIGlnYXMgdmFoZW1pa3VzIGx1Z2VkYSBrb2trdSBzaW5uYSBsYW5nZXZhZCB2YWF0bHVzZWQuIEthcnBkaWFncmFtbWlsIG9uIHZhamEgbGVpZGEgbWVkaWFhbiBqYSBrdmFudGlpbGlkLiBLdWkga2FycGRpYWdyYW1taSBpbG1hIHRlaXNlbmR1c2V0YSBqb29uaXN0YWRhIGVpIHNhYSwgc2lpcyB0dWxwZGlhZ3JhbW1pIHB1aHVsIG9uIG9uIHZhaGVzdCBtw7Vpc3RsaWsgbGFzdGEgYXV0b21hYXRzZWx0IHZhYXRsdXN0ZSBhcnZ1ZCBrb2trdSBsdWdlZGEuIFRlaW5la29yZCBqw6RsbGUgdGFoYWtzIHR1bGJhIGvDtXJndXNlIGlzZSBldHRlIGFuZGEuIFNlbGxla3Mgb24ga8O1aWdpbCBgZ2VvbV8qYCBrw6Rza3VkZWwgYHN0YXRgIHBhcmFtZWV0ZXIuIGBnZW9tX2JhcmAgYWJpbGVoZWx0IG9uIG7DpGhhLCBldCB2YWlraW1pcyB2w6TDpHJ0dXMgc2VsbGVsIHBhcmFtZWV0cmlsIG9uIGAiY291bnQiYC4gU2VlIHTDpGhlbmRhYiwgZXQga3VpIGFubmFtZSBldHRlIGRpc2tyZWV0c2UgdHVubnVzZSBsb2V0YWtzZSBrb2trdSBpZ2EgdsOkw6RydHVzZWdhIHZhYXRsdXNlZCBqYSBqb29uaXN0YXRha3NlIG5lZWQuDQoNCmBgYHtyfQ0KZ2dwbG90KGxpbm5hZCwgYWVzKHggPSBTdGF0ZSkpICsNCiAgZ2VvbV9iYXIoKQ0KYGBgDQoNCkt1aSB0YWhhbWUgYWdhIHR1bGJhIGvDtXJndXNlZCBldHRlIGFuZGEgc2FhbWUgZXJyb3JpLCBzZXN0IGBnZW9tX2JhcmAgc29vdmliIHkgdsOkw6RydHVzdCBpc2UgYXJ2dXRhZGEuIFNlbGxla3MgcGVhbWUgbXV1dG1hIHN0YXRpc3RpbGlzZSB0ZWlzZW5kdXNlIG5pbWUgamEgbWVpbGUgc29iaXZha3MgdGVpc2VuZHVzZWtzIG9uIHNpaW5rb2hhbCBgImlkZW50aXR5ImAuDQoNCmBgYHtyfQ0KZ2dwbG90KGxpbm5hZCwgYWVzKHggPSBDb3VudHksIHkgPSBwb3BfZXN0aW1hdGUpKSArDQogIGdlb21fYmFyKCkNCmBgYA0KDQpgYGB7cn0NCmdncGxvdChsaW5uYWQsIGFlcyh4ID0gQ291bnR5LCB5ID0gcG9wX2VzdGltYXRlKSkgKw0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikNCmBgYA0KDQojIyMgUG9zaXRzaW9vbg0KDQrDnGtzIGtpaGkga29tcG9uZW50IG9uIGthIHBvc2l0c2lvb24sIG1pcyBrb250cm9sbGliIHRlYXR1ZCBncmFhZmlsaXN0ZSBlbGVtZW50aWRlIHBhaWd1dHVzLiBFbmFtYXN0aSBrYXN1dGF0YWtzZSBzZWRhIHbDpHJ2aWxpc3RlIHR1bHBkaWFncmFtbWlkZSBqb29uaXN0YW1pc2VsLiBTZWxsZSBwYXJhbWVldHJpIGVyaW5ldmF0ZWwgdsOkw6RydHVzdGVsIG9uIGrDpHJnbWlzZWQgdGFnYWrDpHJqZWQ6DQoNCi0gICBgImRvZGdlImAgLSBwYW5lYsKgIGVyaW5ldmF0IHbDpHJ2aSB0dWxiYWQga8O1cnZ1dGkNCg0KLSAgIGAic3RhY2siYC0gcGFuZWIgZXJpbmV2YXQgdsOkcnZpIHR1bGJhZCDDvGtzdGVpc2Ugb3RzYQ0KDQotICAgYCJmaWxsImAgLSBwYW5lYiB0dWxiYWQgw7xrc3RlaXNlIG90c2EgamEgdGVpc2VuZGFiIMO8aGVrw7VyZ3VzZWtzIChrYXN1bGlrIGt1aSB1dXJpZGEgb3Nha2FhbHVzaWQpDQoNCmBgYHtyfQ0KZ2dwbG90KGxpbm5hZCwgYWVzKHggPSBCaXJ0aF9mYWN0b3IsIGZpbGwgPSBQb3ZlcnR5X2ZhY3RvcikpICsNCiAgZ2VvbV9iYXIocG9zaXRpb24gPSAiZG9kZ2UiKQ0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90KGxpbm5hZCwgYWVzKHggPSBCaXJ0aF9mYWN0b3IsIGZpbGwgPSBQb3ZlcnR5X2ZhY3RvcikpICsNCiAgZ2VvbV9iYXIocG9zaXRpb24gPSAic3RhY2siKQ0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90KGxpbm5hZCwgYWVzKHggPSBCaXJ0aF9mYWN0b3IsIGZpbGwgPSBQb3ZlcnR5X2ZhY3RvcikpICsNCiAgZ2VvbV9iYXIocG9zaXRpb24gPSAiZmlsbCIpDQpgYGANCg0KSGFqdXZ1c2RpYWdyYW1taSBwdWh1bCBzYWFiIGthc3V0YWRhIHbDpMOkcnR1c3QgYCJqaXR0ZXIiYCwgbWlzIGxpc2FiIGFuZG1ldGVsZSBuYXR1a2UgbcO8cmEsIGV0IHB1bmt0aWQgZWkgb2xla3MgbmlpIMO8a3N0ZWlzZSBvdHNhcy7CoA0KDQpgYGB7cn0NCmdncGxvdChsaW5uYWQsIGFlcyh4ID0gQmlydGhfZmFjdG9yLCB5ID0gcGVyY19wb3ZlcnR5KSkgKw0KICBnZW9tX3BvaW50KCkNCmBgYA0KDQpgYGB7cn0NCmdncGxvdChsaW5uYWQsIGFlcyh4ID0gQmlydGhfZmFjdG9yLCB5ID0gcGVyY19wb3ZlcnR5KSkgKw0KICBnZW9tX3BvaW50KHBvc2l0aW9uID0gImppdHRlciIpDQpgYGANCg0KIyMjIE1pdG1lIGtpaGkgbGlzYW1pbmUNCg0KS2lodGUgdsO1aWIgbGlzYWRhIG1pdHUuIFZhaWtpbWlzaSB2w7VldGFrc2UgYW5kbWVkIGphIHNlb3NlZCBhbmRtZXRlIG5pbmcgZ3JhYWZpbGlzdGUgZWxlbWVudGlkZSB2YWhlbCDDvGxhbG9sZXZhc3QgYGdncGxvdGAga8Okc3VzdCwga3VpZCBsb29tdWxpa3V0bCB2w7VpYiB1dXNpIGtpaHRlIHTDpGllbmRhZGEgbmlpIG5hZ3Ugw7xsYWwga2lyamVsZGF0dWQuDQoNCmBgYHtyfQ0KZ2dwbG90KGxpbm5hZCwgYWVzKHggPSBwZXJfY2FwaXRhX2luYywgeSA9IHVuZW1wbG95bWVudF9yYXRlLCBjb2xvciA9IGJhY2hlbG9yKSkgKyANCiAgZ2VvbV9wb2ludCgpICsgDQogIGdlb21fc21vb3RoKCkNCmBgYA0KDQpLaWh0aWRlbGUgdsO1aWIgZXR0ZSBhbmRhIGthIHV1c2kgYW5kbWVpZCBwYXJhbWVldHJpZ2EgYGRhdGFgLg0KDQpgYGB7cn0NCmxpYnJhcnkodGlkeXZlcnNlKQ0KDQpnZ3Bsb3QobGlubmFkLCBhZXMoeCA9IHBlcl9jYXBpdGFfaW5jLCB5ID0gdW5lbXBsb3ltZW50X3JhdGUsIGNvbG9yID0gYmFjaGVsb3IpKSArIA0KICBnZW9tX3BvaW50KCkgKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsID0gQ291bnR5KSwgY29sb3IgPSAicmVkIiwgaGp1c3QgPSAwLCBkYXRhID0gbGlubmFkICU+JSBmaWx0ZXIocG9wX2VzdGltYXRlID4gMzAwMDAwMCkpDQpgYGANCg0KIyMjIyDDnGxlc2FuZGVkDQoNCi0gICBKb29uaXN0YSB0dWxwZGlhZ3JhbW1lIG1pcyB1dXJpdmFkIHR1bm51c2UgKkJpcnRoX2ZhY3RvciogamFvdHVzdCBvc2FyaWlraWRlIGthdXBhLiBNaWxsaW5lIHBvc2l0aW9uIHbDpMOkcnR1cyBhbm5hYiBrw7VpZ2UgbcO1aXN0bGlrdW1hIGdyYWFmaWt1Pw0KDQotICAgSm9vbmlzdGEgaGFqdXZ1c2RpYWdyYW1tIGtlc2trb29saSBqYSDDvGxpa29vbGkgbMO1cGV0YW51dGUgcHJvdHNlbmRpIHZhaGVswqAoKmhpZ2hfc2NsKiBqYSAqYmFjaGVsb3IqKS4NCg0KLSAgIExpc2EgbmVpbGUgbWFha29uZGFkZWxlIG5pbWVkLCBrdXMga2Vza2tvb2xpIGhhcmlkdXNlZ2EgaW5pbWVzdGUgb3Nha2FhbCBvbiBhbGxhIDUwLWUuDQoNCi0gICBMaXNhIGdyYWFmaWt1bGUgdmVydGlrYWFsbmUgam9vbiBrb2hhbCB4ID0gNTAuICh2aWhqZToga2FzdXRhIGvDpHNrdSBgZ2VvbV92bGluZWApDQoNCmBgYHtyfQ0KDQpnZ3Bsb3QobGlubmFkLCBhZXMoeCA9IEJpcnRoX2ZhY3RvciwgZmlsbCA9IFN0YXRlKSkgKw0KICBnZW9tX2Jhcihwb3NpdGlvbiA9ICJkb2RnZSIpDQoNCmdncGxvdChsaW5uYWQsIGFlcyh4ID0gQmlydGhfZmFjdG9yLCBmaWxsID0gU3RhdGUpKSArDQogIGdlb21fYmFyKHBvc2l0aW9uID0gInN0YWNrIikNCg0KZ2dwbG90KGxpbm5hZCwgYWVzKHggPSBCaXJ0aF9mYWN0b3IsIGZpbGwgPSBTdGF0ZSkpICsNCiAgZ2VvbV9iYXIocG9zaXRpb24gPSAiZmlsbCIpDQpgYGANCg0KYGBge3J9DQpnZ3Bsb3QobGlubmFkLCBhZXMoeSA9IGhpZ2hfc2NsICwgeD0gYmFjaGVsb3IpKSArIA0KICBnZW9tX3BvaW50KCkgKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsID0gQ291bnR5KSwgY29sb3IgPSAicmVkIiwgaGp1c3QgPSAwLCBkYXRhID0gbGlubmFkICU+JSBmaWx0ZXIoaGlnaF9zY2wgPCA1MCkpICsNCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gNTApDQpgYGANCg0KIyMgKipHcmFhZmlrdSBqYWdhbWluZSB0YWhrdWRla3MqKg0KDQpUaWh0aSBvbiBrYXN1bGlrIMO8aGUgc3V1cmUgcGlsZGkgYXNlbWVsIGpvb25pc3RhZGEgcGFsanUgdsOkaWtlc2VpZCBzYXJhbnNlIHNpc3VnYSBwaWx0ZSwga3VzanV1cmVzIG9iamVrdGlkZSBqYWdhbWluZSBha2VuZGVzZSBvbiB0b2ltdWIgbWluZ2kgdHVubnVzZSB2w6TDpHJ0dXN0ZSBww7VoamFsLsKgU2VkYSBzYWFiIHNhYWF2dXRhZGEga8Okc2t1ZGVnYSBgZmFjZXRfZ3JpZGAgamEgYGZhY2V0X3dyYXBgLCBuZWlzdCBlc2ltZW5lIHBhaWd1dGFiIHBpbHRlIHJpZGFkZXNzZSBqYSB2ZWVyZ3VkZXNzZSBncmFhZmlrdXRlIG1hYXRyaWtzaXMgbmluZyB0ZWluZSBwcm9vdmliIHBhaWd1dGFkYSDDvGhlIG11dXR1amEgcMO1aGphbCBqYXRhdHVkIGdyYWFmaWt1ZCB2w7VpbWFsaWt1bHQga29tcGFrdHNlbHQuDQoNCmBmYWNldF9ncmlkYCBwdWh1bCB0dWxlYiBldHRlIGFuZGEgcGFyYW1lZXRlciBrdWp1bCBgPHJpZGFkZWtzIGphZ2F2IG11dXR1amE+IH4gPHZlZXJndWRla3MgamFnYXYgbXV1dHVqYT5gLCBrdWkgw7xrcyBuZWlzdCDDpHJhIGrDpHR0YSwgc2lpcyB0dWxlYiBzZWxsZSBhc2VtZWxlIGtpcmp1dGFkYSBwdW5rdC4NCg0KYGBge3J9DQpnZ3Bsb3QobGlubmFkLCBhZXMoeCA9IHBlcl9jYXBpdGFfaW5jLCB5ID0gdW5lbXBsb3ltZW50X3JhdGUsIGNvbG9yID0gYmFjaGVsb3IpKSArIA0KICBnZW9tX3BvaW50KCkgKw0KICBmYWNldF9ncmlkKFN0YXRlIH4gLikNCmBgYA0KDQpgYGB7cn0NCmdncGxvdChsaW5uYWQsIGFlcyh4ID0gcGVyX2NhcGl0YV9pbmMsIHkgPSB1bmVtcGxveW1lbnRfcmF0ZSwgY29sb3IgPSBiYWNoZWxvcikpICsgDQogIGdlb21fcG9pbnQoKSArDQogIGZhY2V0X2dyaWQoLiB+IFN0YXRlKQ0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90KGxpbm5hZCwgYWVzKHggPSBwZXJfY2FwaXRhX2luYywgeSA9IHVuZW1wbG95bWVudF9yYXRlLCBjb2xvciA9IGJhY2hlbG9yKSkgKyANCiAgZ2VvbV9wb2ludCgpICsNCiAgZmFjZXRfZ3JpZChCaXJ0aF9mYWN0b3IgfiBTdGF0ZSkNCmBgYA0KDQpgZmFjZXRfd3JhcGAgcHVodWwgYW50YWtzZSBwYXJhbWVldGVyIGV0dGUga3VqdWwgYH4gPG11dXR1amEgbmltaT4uYA0KDQpgYGB7cn0NCmdncGxvdChsaW5uYWQsIGFlcyh4ID0gcGVyX2NhcGl0YV9pbmMsIHkgPSB1bmVtcGxveW1lbnRfcmF0ZSwgY29sb3IgPSBiYWNoZWxvcikpICsgDQogIGdlb21fcG9pbnQoKSArDQogIGZhY2V0X3dyYXAofiBTdGF0ZSkNCmBgYA0KDQojIyBTa2FsZWVyaW1pbmUNCg0KRXQgbXV1dGEgdmlpc2ksIGt1aWRhcyBgZ2dwbG90MmAgbXV1dHVqYWlkIGdyYWFmaWxpc2V0ZWtzIHBhcmFtZWV0cml0ZWtzIHRlaXNlbmRhYiBzYWFiIGthc3V0YWRhIGvDpHNrZSBgc2NhbGVfPGdyYWFmaWxpbmUgcGFyYW1lZXRlcj5fPHNrYWFsYSBuaW1pPmAuIE7DpGl0ZWtzIGZ1bmt0c2lvb25pIGBzY2FsZV94X2NvbnRpbnVvdXNgIHNhYWIga2FzdXRhZGEgeC10ZWxqZWwgYXN1dmEgcGlkZXZhIHR1bm51c2Ugc2thbGVlcmltaXNla3MgamEgYHNjYWxlX2NvbG91cl9ncmV5YCBvbiB2w6Rydmkgc2thbGVlcmltaXNla3MgbXVzdHZhbGdlbCBza2FhbGFsLiBLw7Vpa2kgcmVsZXZhbnRzZWlkIHNrYWxlZXJpbWlzZnVua3RzaW9vbmUgb24gdsO1aW1hbGlrIG7DpGhhIGBnZ3Bsb3QyYCBrb2R1bGVoZWwgPGh0dHBzOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS9pbmRleC5odG1sI3NlY3Rpb24tc2NhbGVzPi4NCg0KU2thbGVlcmltaXNmdW5rdHNpb29uaSByYWtlbmRhbWlzZWtzIHR1bGViIHRhIGxpaXRhIGdyYWFmaWt1bGUuwqANCg0KYGBge3J9DQpnZ3Bsb3QobGlubmFkLCBhZXMoeCA9IHBlcl9jYXBpdGFfaW5jLCB5ID0gdW5lbXBsb3ltZW50X3JhdGUsIGNvbG9yID0gYmFjaGVsb3IpKSArIA0KICBnZW9tX3BvaW50KCkNCmBgYA0KDQpgYGB7cn0NCmdncGxvdChsaW5uYWQsIGFlcyh4ID0gcGVyX2NhcGl0YV9pbmMsIHkgPSB1bmVtcGxveW1lbnRfcmF0ZSwgY29sb3IgPSBiYWNoZWxvcikpICsgDQogIGdlb21fcG9pbnQoKSArDQogIHNjYWxlX3hfY29udGludW91cyh0cmFucyA9ICJsb2cxMCIpDQpgYGANCg0KSWdhIGVyaW5ldmEgcGFyYW1lZXRyaSBza2FsZWVyaW1pc2VrcyBzYWFiIGdyYWFmaWt1bGUgbGlpdGEgdXVlIGZ1bmt0c2lvb25pLiBLb25rcmVldHNldGUgZnVua3RzaW9vbmlkZSBwYXJhbWVldHJpZCBzw7VsdHV2YWQgcGFsanUgdHVubnVzZSB0w7zDvGJpc3QgamEgZ3JhYWZpbGlzZSBwYXJhbWVldHJpIG9tYWR1c3Rlc3QsIGt1aWQgc2VsbGVnaXBvb2xlc3Qgb24gb2xlbWFzIG3DtW5lZCBhcmd1bWVuZGlkLCBtaWRhIGvDtWlrIHNrYWxlZXJpbWlzZnVua3RzaW9vbmlkIHR1bm5pc3RhdmFkLg0KDQotICAgYG5hbWVgIC0gdGVsZ2VkZSBwdWh1bCB0ZWxqZSBuaW1pLCB2w6RydmlkZSwga3VqdSBqbmUga29ycmFsIG5pbWV0YXRha3NlIHNlbGxlZ2EgdmFzdGF2YXQgbGVnZW5kaS4NCg0KLSAgIGBicmVha3NgIC0gdmVrdG9yIHB1bmt0aWRlZ2EsIG1pbGxlbCBza2FhbGF0IGtpcmplbGRhdGFrc2UuIE7DpGl0ZWtzIHRlbGdkZSBwdWh1bCwgbWlsbGlzdGUgdsOkw6RydHVzdGUganV1cmVzIG9uIHZhc3RhdmFsIHRlbGplbCDDpHJhIG7DpGlkYXR1ZCBudW1icmlsaXNlZCB2w6TDpHJ0dXNlZC4gVsOkcnZpIHB1aHVsIG7DpGl0YWIgc2VlLCBldCBtaWxsaXNlZMKgIHR1bm51c2UgdsOkw6RydHVzZWQgb24gdG9vZHVkIGxlZ2VuZGlzLsKgDQoNCi0gICBgbGFiZWxzYCAtIHBhcmFtZWV0cmlnYSBicmVha3MgbcOkw6RyYXR1ZCBwdW5rdGlkZSBzaWxkaWQuwqANCg0KYGBge3J9DQpnZ3Bsb3QobGlubmFkLCBhZXMoeCA9IHBlcl9jYXBpdGFfaW5jLCB5ID0gdW5lbXBsb3ltZW50X3JhdGUsIGNvbG9yID0gU3RhdGUpKSArIA0KICBnZW9tX3BvaW50KCkgKw0KICBzY2FsZV94X2NvbnRpbnVvdXMobmFtZSA9ICJQZXIgY2FwaXRhIGluY29tZSIsIGJyZWFrcyA9IGMoMTAwMDAsIDQwMDAwKSkNCmBgYA0KDQpgYGB7cn0NCmdncGxvdChsaW5uYWQsIGFlcyh4ID0gcGVyX2NhcGl0YV9pbmMsIHkgPSB1bmVtcGxveW1lbnRfcmF0ZSwgY29sb3IgPSBTdGF0ZSkpICsgDQogIGdlb21fcG9pbnQoKSArDQogIHNjYWxlX3hfY29udGludW91cyhuYW1lID0gIlBlciBjYXBpdGEgaW5jb21lIiwgYnJlYWtzID0gYygxMDAwMCwgNDAwMDApLCBsYWJlbHMgPSBjKCJTbWFsbCIsICJMYXJnZSIpKQ0KYGBgDQoNCiMjIyBQaWRldmF0ZSB0dW5udXN0ZSBza2FsZWVyaW1pbmUNCg0KUGlkZXZhdGUgbXV1dHVqYXRlIHNrYWxlZXJpbWlzZWtzIGthc3V0YXRhdmF0ZWwgZnVua3RzaW9vbmlkZWwgKG7DpGl0LiBgc2NhbGVfKl9jb250aW51b3VzYCwgYHNjYWxlXypfZ3JhZGllbnRgLCAuLi4pIG9uIG3DtW5lZCBzcGV0c2lpZmlsaXNlZCBhcmd1bWVuZGlkLg0KDQotICAgYHRyYW5zYCAtIHNhYW1lIGFuZGEgZXR0ZSB0cmFuc2Zvcm1hdHNpb29uaSBuaW1lLCBtaWRhIHJha2VuZGFkYSB2w6TDpHJ0dXN0ZWxlIGVubmUgcGlsZGlsZSBwYW5lbWlzdC4gTcO1bmVkIHbDtWltYWxpa3VkIHbDpMOkcnR1c2VkOiBgImV4cCJgLCBgImxvZzIiYCwgYCJsb2cxMCJgLCBgInBvdzEwImAsIGAic3FydCJgLiAuLi4gLg0KDQotICAgYGxpbWl0c2AgLSBrYWhlIGVsZW1lbmRpbGluZSB2ZWt0b3IsIG1pcyBtw6TDpHJhYiDDpHJhIHRlbGplIHbDpGhpbWEgamEgc3V1cmltYSBwdW5rdGkuDQoNCmBgYHtyfQ0KZ2dwbG90KGxpbm5hZCwgYWVzKHggPSBwZXJfY2FwaXRhX2luYywgeSA9IHVuZW1wbG95bWVudF9yYXRlLCBjb2xvdXIgPSBTdGF0ZSkpICsNCiAgZ2VvbV9wb2ludCgpICsgDQogIHNjYWxlX3hfY29udGludW91cyh0cmFucyA9ICJsb2cxMCIpDQpgYGANCg0KYGBge3J9DQpnZ3Bsb3QobGlubmFkLCBhZXMoeCA9IHBlcl9jYXBpdGFfaW5jLCB5ID0gdW5lbXBsb3ltZW50X3JhdGUsIGNvbG91ciA9IFN0YXRlKSkgKw0KICBnZW9tX3BvaW50KCkgKyANCiAgc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cyA9IGMoMCwgMTAwMDAwKSkNCmBgYA0KDQojIyMgRGlza3JlZXRzZXRlIG11dXR1amF0ZSBza2FsZWVyaW1pbmUNCg0KRGlza3JlZXRzZXRlIG11dXRqYXRlIHNrYWxlZXJpbWlzZWtzIGthc3V0YXRha3NlIGZ1bmt0c2lvb25pIGBzY2FsZV8qX2Rpc2NyZXRlYC4gUGFyYW1lZXRlciBsaW1pdHMgdMO2w7Z0YWIgZGlza3JlZXRldGUgbXV1dHVqYXRlIHB1aHVsIHRlaXNpdGksIGt1aSBwaWRldmFsIGp1aHVsLiBLdW5hIG1lIGVpIHNhYSBldHRlIGFuZGEgdsOkw6RydHVzdGUgdmFoZW1pa3Ugb3RzcHVua3RlLCBzaWlzIHBlYW1lIGV0dGUgYW5kbWEga8O1aWsgdsOkw6RydHVzZWQsIG1pZGEgbWUgdGFoYW1lIGt1anV0YWRhLCBrb29zIGrDpHJqZWtvcnJhZ2EuDQoNCmBgYHtyfQ0KZ2dwbG90KGxpbm5hZCwgYWVzKHggPSBTdGF0ZSwgeSA9IHVuZW1wbG95bWVudF9yYXRlKSkgKw0KICBnZW9tX2JveHBsb3QoKSArIA0KICBzY2FsZV94X2Rpc2NyZXRlKGxpbWl0cyA9IGMoIlRleGFzIiwgIk1hcnlsYW5kIikpDQpgYGANCg0KIyMjIFbDpHJ2aWRlIHNrYWxlZXJpbWluZQ0KDQpWw6RydmlkZSB2YWxpayBvbiBncmFhZmlrdXRlIHB1aHVsIHbDpGdhIG9sdWxpbmUuIMOVaWdlc3RpIHZhbGl0dWQgdsOkcnZpZGVnYSBvbiB2w7VpbWFsaWsgdHV1YSBzZWxnZW1pbmkgdsOkbGphIG9tYSBzw7VudW1pdCwgbXV1dGEgZ3JhYWZpa3V0IGxvZXRhdmFtYWtzIGphIGthIHZpc3VhYWxzZWx0IG1lZWxkaXZhbWFrcyBuaW5nIHByb2Zlc3Npb25hYWxzZW1ha3MuwqAgQXNqYWtvaGFuZSB2w6Rydmlza2FhbGEgc8O1bHR1YiBqw6RsbGVnaSBwYWxqdSB0dW5udXNlc3QgbWlkYSB0YWhhbWUga3VqdXRhZGEuwqAgVsOkZ2Egw7xsZGlzZWx0IHbDtWliIHbDpHJ2aXNrYWFsYWQgamFnYWRhIGtvbG1la3M6wqANCg0KLSAgIGdyYWRpZW50IC0ga2FzdXRhdGFrc2UgcGlkZXZhdGUgdHVubnVzdGUga3VqdXRhbWlzZWwsIGvDtWlnZSB2w6Rpa3NlbSB2w6TDpHJ0dXMgamEgc3V1cmVtIHbDpMOkcnR1cyB2YXN0YXZhZCBtaW5naXRlbGUgdsOkcnZpZGVsZSBuaW5nIHZhaGVwZWFsc2VkIHbDpMOkcnR1c2VkIHNpaXMgbmVuZGUga2FoZSB2w6Rydmkgc2VndWRlbGU7DQoNCi0gICBsYWhrbmV2IGdyYWRpZW50IC0ga3VpIHBpZGV2YWwgdHVubnVzZWwgb24gbWluZ2kgc2VsZ2UgbnVsbHB1bmt0LCBzaWlzIHNhYWIgc2VkYSB2aXN1YWFsc2VsdCBrdWp1dGFkYSBrb2xtZWFzdG1lbGlzZSBncmFkaWVuZGlnYSwga3VzIGtha3MgdsOkcnZpIG9uIHJlc2VydmVlcml0dWQgZWtzdHJlZW1zZXRlbGUgdsOkw6RydHVzdGVsZSBqYSDDvGtzIHNpaXMgbm4gbnVsbHB1bmt0aSBqYW9rcy4gTsOkaXRla3Mga3VpIGt1anV0YW1lIHRlbXBlcmF0dXVyZSwgb24gbG9vbXVsaWsga3VqdXRhZGEgcG9zaXRpaXZzZWlkIHbDpMOkcnR1c2kgZXJpbmV2YSB0dWdldnVzZWdhIHB1bmFzdGVnYSBqYSBuZWdhdGlpdnNlaWQgc2FtYW1vb2RpIHNpbmlzZSB2YXJqdW5kaXRlZ2E7DQoNCi0gICBrdmFsaXRhdGlpdm5lIC0gaWdhIHbDpMOkcnR1c2Uga3VqdXRhbWlzZWtzIGthc3V0YXRha3NlIHbDtWltYWxpa3VsdCBlcmluZXZhdCB2w6Rydml0b29uaSwgc2FtYXMgb24gb2x1bGluZSBzaWxtYXMgcGlkYWRhLCBldCBoZWxlZHVzZWx0IGphIHbDpHJ2aSB0dWdldnVzZWx0IG9sZWtzaWQga8O1aWsgdG9vbmlkIHNhcm5hc2VkLCBzZXN0IG11aWR1IGhha2thdmFkIHRlYXR1ZCB2w6TDpHJ0dXNlZCB0ZWlzdGUgw7xsZSBkb21pbmVlcmltYTsNCg0KS8O1aWtpIG5laWQgdmFyaWFudGUgb24gZ2dwbG90aWdhIHN1aHRlbGlzZWx0IGxpaHRuZSBzYWF2dXRhZGEuIEdyYWRpZW5kaSBrb250cm9sbGltaXNla3Mgc2FhYiBrYXN1dGFkYSBmdW5rdHNpb29uaSBgc2NhbGVfKl9ncmFkaWVudGAuIFNlYWwgdMO2w7Z0YXZhZCBrw7Vpa2UgZWVscG9vbCB0dXR2dXN0YXR1ZCBwaWRldmF0ZSBza2FsZWVyaW1pc2Z1bmt0c2lvb25pZGUgYXJndW1lbmRpZCBuaW5nIGxpc2FrcyBvbiBrYWtzIHBhcmFtZWV0cml0IGBsb3dgIGphIGBoaWdoYCwgbWlzIG3DpMOkcmF2YWQgw6RyYSBzaWlzIGdyYWRpZW5kaSBhbGd1c2UgamEgbMO1cHUgdsOkcnZpLg0KDQpgYGB7cn0NCmdncGxvdChsaW5uYWQsIGFlcyh4ID0gcGVyX2NhcGl0YV9pbmMsIHkgPSB1bmVtcGxveW1lbnRfcmF0ZSwgY29sb3VyID0gYmFjaGVsb3IpKSArDQogIGdlb21fcG9pbnQoKQ0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90KGxpbm5hZCwgYWVzKHggPSBwZXJfY2FwaXRhX2luYywgeSA9IHVuZW1wbG95bWVudF9yYXRlLCBjb2xvdXIgPSBiYWNoZWxvcikpICsNCiAgZ2VvbV9wb2ludCgpICsgDQogIHNjYWxlX2NvbG91cl9ncmFkaWVudChuYW1lID0gIlBlcmMuIGJhY2hlbG9yIiwgbG93ID0gInllbGxvdyIsIGhpZ2ggPSAicmVkIikNCmBgYA0KDQpgYGB7cn0NCmdncGxvdChsaW5uYWQsIGFlcyh4ID0gcGVyX2NhcGl0YV9pbmMsIHkgPSB1bmVtcGxveW1lbnRfcmF0ZSwgY29sb3VyID0gYmFjaGVsb3IpKSArDQogIGdlb21fcG9pbnQoKSArIA0KICBzY2FsZV9jb2xvdXJfZ3JhZGllbnQobmFtZSA9ICJQZXJjLiBiYWNoZWxvciIsIGxvdyA9ICJ5ZWxsb3ciLCBoaWdoID0gInJlZCIsIGJyZWFrcyA9IGMoMTAsIDMwLCA1MCkpDQpgYGANCg0KTGFoa25ldmF0IGphIHZlZWwga2VlcnVsaXNlbWFpZCBncmFkaWVudGUgc2FhYiBmdW5rdHNpb29uaWdhIGBzY2FsZV8qX2dyYWRpZW50bmAsIG1pbGxlIHBhcmFtZWV0cmlsZSBgY29sb3JzYCBzYWFiIGV0dGUgYW5kYSB2ZWt0b3JpIHbDpHJ2aWRlZ2EgbWlsbGUgdmFoZWxlIHRhIHNpaXMgdXVlZCB2w6RydmlkIGludGVycG9sZWVyaWIuDQoNCmBgYHtyfQ0KZ2dwbG90KGxpbm5hZCwgYWVzKHggPSBwZXJfY2FwaXRhX2luYywgeSA9IHVuZW1wbG95bWVudF9yYXRlLCBjb2xvdXIgPSBiYWNoZWxvciAtIG1lYW4oYmFjaGVsb3IpKSkgKw0KICBnZW9tX3BvaW50KCkNCmBgYA0KDQpgYGB7cn0NCmdncGxvdChsaW5uYWQsIGFlcyh4ID0gcGVyX2NhcGl0YV9pbmMsIHkgPSB1bmVtcGxveW1lbnRfcmF0ZSwgY29sb3VyID0gYmFjaGVsb3IgLSBtZWFuKGJhY2hlbG9yKSkpICsNCiAgZ2VvbV9wb2ludCgpICsgDQogIHNjYWxlX2NvbG91cl9ncmFkaWVudG4obmFtZSA9ICJQZXJjLiBiYWNoZWxvciIsIGNvbG91cnMgPSBjKCJ5ZWxsb3ciLCAid2hpdGUiLCAicmVkIikpDQpgYGANCg0KYGBge3J9DQpnZ3Bsb3QobGlubmFkLCBhZXMoeCA9IHBlcl9jYXBpdGFfaW5jLCB5ID0gdW5lbXBsb3ltZW50X3JhdGUsIGNvbG91ciA9IGJhY2hlbG9yIC0gbWVhbihiYWNoZWxvcikpKSArDQogIGdlb21fcG9pbnQoKSArIA0KICBzY2FsZV9jb2xvdXJfZ3JhZGllbnRuKG5hbWUgPSAiUGVyYy4gYmFjaGVsb3IiLCBjb2xvdXJzID0gYygieWVsbG93IiwgIndoaXRlIiwgInJlZCIpLCBsaW1pdHMgPSBjKC00MCwgNDApKQ0KYGBgDQoNCkRpc2tyZWV0c2V0ZS9rdmFsaXRhdGlpdnNldGUgdsOkw6RydHVzdGUgcHVodWwga2FzdXRhdGFrc2UgdmFpa2ltaXNpIGZ1bmt0c2lvb25pIGBzY2FsZV8qX2h1ZWAsIG1pcyB2YWxpYiBIQ0wgdsOkcnZpc2thYWxhbCwgcGFyYW1lZXRyaSBodWUgdsO1aW1hbGlrdWx0IGVyaW5ldmFkIHbDpMOkcnR1c2VkIGrDpHR0ZXMgdsOkcnZpIHR1Z2V2dXNlIGphIGhlbGVkdXNlIGtvbnN0YW50c2Vrcy4gTmlpIHNhYW1lIHbDtWltYWxpa3VsdCBlcmluZXZhZCB2w6RydmlkLCBtaXMgc2FtYWwgYWphbCBwZWFrcyBvbGVtYSBzYXJuYXNlIGludGVuc2lpdnN1c2VnYS7CoA0KDQpLw7VpZ2Uga2FzdWxpa3VtIGZ1bmt0c2lvb24gb24gdmFzdCBgc2NhbGVfKl9icmV3ZXJgLCBrdXMgc2FhYiB2YWxpZGEgYWxnc2VsdCBtYWFrYWFydGlkZSB2w6RydmltaXNla3MgYWVuZGF0dWQgdsOkcnZpcGFsZXR0ZSAoPGh0dHA6Ly9jb2xvcmJyZXdlcjIub3JnPi8pLiBTZWFsIG9uIGtha3MgcGFyYW1lZXRyaXQgdHlwZSBqYSBwYWxldHRlLiBBcmd1bWVuZGkgdHlwZSB2w7VpbWFsaWx1ZCB2w6TDpHJ0dXNlZCBvbiBg4oCcc2Vx4oCdYCwgYOKAnGRpduKAnWAgamEgYOKAnHF1YWzigJ1gLCBtaXMgc2lpcyB0w6RoaXN0YXZhZCBlcmluZXZhaWQgcGFsZXRpIHTDvMO8cGUuIFBhbGV0dCB2w6TDpHJ0dXNla3Mgc2FhYiBhbmRhIHBhbGV0aSBudW1icmkuwqANCg0KRnVua3RzaW9vbiBgc2NhbGVfKl9icmV3ZXJgIGVlbGRhYiBkaXNrcmVldHNlaWQgYW5kbWVpZC4NCg0KYGBge3J9DQpnZ3Bsb3QobGlubmFkLCBhZXMoeCA9IHBlcl9jYXBpdGFfaW5jLCB5ID0gdW5lbXBsb3ltZW50X3JhdGUsIGNvbG91ciA9IFN0YXRlKSkgKw0KICBnZW9tX3BvaW50KCkgKyANCiAgc2NhbGVfY29sb3VyX2JyZXdlcih0eXBlID0gInF1YWwiLCBwYWxldHRlID0gNikNCmBgYA0KDQpgYGB7cn0NCmdncGxvdChsaW5uYWQsIGFlcyh4ID0gcGVyX2NhcGl0YV9pbmMsIHkgPSB1bmVtcGxveW1lbnRfcmF0ZSwgY29sb3VyID0gU3RhdGUpKSArDQogIGdlb21fcG9pbnQoKSArIA0KICBzY2FsZV9jb2xvdXJfYnJld2VyKHR5cGUgPSAicXVhbCIsIHBhbGV0dGUgPSA3KQ0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90KGxpbm5hZCwgYWVzKHggPSBwZXJfY2FwaXRhX2luYywgeSA9IHVuZW1wbG95bWVudF9yYXRlLCBjb2xvdXIgPSBiaXJ0aF9jbGFzcykpICsNCiAgZ2VvbV9wb2ludCgpICsgDQogIHNjYWxlX2NvbG91cl9icmV3ZXIodHlwZSA9ICJzZXEiLCBwYWxldHRlID0gMSkNCmBgYA0KDQpgYGB7cn0NCmdncGxvdChsaW5uYWQsIGFlcyh4ID0gcGVyX2NhcGl0YV9pbmMsIHkgPSB1bmVtcGxveW1lbnRfcmF0ZSwgY29sb3VyID0gYmlydGhfY2xhc3MpKSArDQogIGdlb21fcG9pbnQoKSArIA0KICBzY2FsZV9jb2xvdXJfYnJld2VyKHR5cGUgPSAic2VxIiwgcGFsZXR0ZSA9IDIpDQpgYGANCg0KYGBge3J9DQpnZ3Bsb3QobGlubmFkLCBhZXMoeCA9IHBlcl9jYXBpdGFfaW5jLCB5ID0gdW5lbXBsb3ltZW50X3JhdGUsIGNvbG91ciA9IGJpcnRoX2NsYXNzKSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICBzY2FsZV9jb2xvdXJfYnJld2VyKHR5cGUgPSAiZGl2IiwgcGFsZXR0ZSA9IDEpDQpgYGANCg0KYGBge3J9DQpnZ3Bsb3QobGlubmFkLCBhZXMoeCA9IHBlcl9jYXBpdGFfaW5jLCB5ID0gdW5lbXBsb3ltZW50X3JhdGUsIGNvbG91ciA9IGJpcnRoX2NsYXNzKSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICBzY2FsZV9jb2xvdXJfYnJld2VyKHR5cGUgPSAiZGl2IiwgcGFsZXR0ZSA9IDMpIA0KYGBgDQoNClBvcHVsYWFybmUgb24ga2EgKnZpcmlkaXMqIHBhbGV0dCAoPGh0dHBzOi8vYmlkcy5naXRodWIuaW8vY29sb3JtYXAvPiksIG1pcyBhbGdzZWx0IG9uIGFyZW5kYXR1ZCBtYXRwbG90bGliIHBha2V0aWxlIHB5dGhvbmlzLiBTZWxsZSBlZWxpcyBvbiwgZXQgZ3JhZGllbmRpZCBsaWlndXZhZCBsw6RiaSBtaXRtZXRlIHbDpHJ2aXRvb25pZGUgbmluZyBuaWkgbXV1dHV2YWQgdsOkw6RydHVzZWQgcGFyZW1pbmkgZXJpc3R1dmFrcy4gU2VsbGVnaXBvb2xlc3Qgc29iaXZhZCBuZWVkIGdyYWRpZW5kaWQga2EgdsOkcnZpcGltZWRhdGVsZSBqYSBtdXN0dmFsZ2VrcyB0csO8a2tpbWlzZWtzLg0KDQpTa2FhbGEgc29iaWIgbmlpIGRpc2tyZWV0c2V0ZSBrdWkgcGlkZXZhdGUgdHVubnVzdGUgamFva3MsIHNrYWFsYSBmdW5rdHNpb29uaWQgb24gc2lpcyB2YXN0YXZhbHQgYHNjYWxlXypfdmlyaWRpc19kYCBqYSBgc2NhbGVfKl92aXJpZGlzX2NgLiBQYWxldGkgdmVyc2lvb25pZGVrcyBvbjogYCJtYWdtYSJgLCBgImluZmVybm8iYCAsIGAicGxhc21hImAgLCBgInZpcmlkaXMiYCAsIGAiY2l2aWRpcyJgICwgYCJyb2NrZXQiYCAsIGAibWFrbyJgIGphIGAidHVyYm8iYCAsIHbDtWkgc2lpcyB2YXN0YXZhbHQgc3V1cmVkIHTDpGhlZCBgIkEiYCAtIGAiSCJgIG1pbGxlIHNhYWIgZXR0ZSBhbmRhIGBvcHRpb25gIHBhcmFtZWV0cmlsZS4NCg0KYGBge3J9DQpnZ3Bsb3QobGlubmFkLCBhZXMoeCA9IHBlcl9jYXBpdGFfaW5jLCB5ID0gdW5lbXBsb3ltZW50X3JhdGUsIGNvbG91ciA9IGJhY2hlbG9yIC0gbWVhbihiYWNoZWxvcikpKSArDQogIGdlb21fcG9pbnQoKSArIA0KICBzY2FsZV9jb2xvdXJfdmlyaWRpc19jKG5hbWUgPSAiUGVyYy4gYmFjaGVsb3IiKQ0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90KGxpbm5hZCwgYWVzKHggPSBwZXJfY2FwaXRhX2luYywgeSA9IHVuZW1wbG95bWVudF9yYXRlLCBjb2xvdXIgPSBiYWNoZWxvciAtIG1lYW4oYmFjaGVsb3IpKSkgKw0KICBnZW9tX3BvaW50KCkgKyANCiAgc2NhbGVfY29sb3VyX3ZpcmlkaXNfYyhuYW1lID0gIlBlcmMuIGJhY2hlbG9yIiwgb3B0aW9uID0gImluZmVybm8iKQ0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90KGxpbm5hZCwgYWVzKHggPSBwZXJfY2FwaXRhX2luYywgeSA9IHVuZW1wbG95bWVudF9yYXRlLCBjb2xvdXIgPSBTdGF0ZSkpICsNCiAgZ2VvbV9wb2ludCgpICsgDQogIHNjYWxlX2NvbG91cl92aXJpZGlzX2Qob3B0aW9uID0gImNpdmlkaXMiKQ0KYGBgDQoNCiMjIyMgw5xsZXNhbmRlZA0KDQotICAgSsOkdGthdGVzIGVlbG1pc3RlcyDDvGxlc2FubmV0ZSBhbHVzdGF0dWQgZGlhZ3JhbW1pIHR1bm51c3RlICpiYWNoZWxvciogamEgKmhpZ2hfc2NsKiB2YWhlbC4gUGFuZSB2YXN0YXZhIHRlbGplIG5pbWVrcyAiSGlnaGVyIGVkdWNhdGlvbiBwZXJjZW50YWdlIjsgbXV1ZGEgdGVsamUgdWxhdHVzdCB2YWhlbWlra3UgKDAsIDEwMCk7IG11dWRhIHNpbHRlIGphIG5lbmRlIHBhaWd1dHVzdCBuaWksIGV0IG5lZWQgb2xla3NpZCBrdWp1bCAwJSwgMjUlLCA1MCUsIDc1JSAxMDAlLg0KDQo8IS0tIC0tPg0KDQotICAgTGlzYSBwaWxkaWxlIHbDpHJ2aWdhIGthIHR1bm51cyBpbmNvbWVfY2xhc3MgbmlpLCBldCBuw6RpZGF0YWtzZSBhaW51bHQgcHVua3RlIGt1cyBzaXNzZXR1bGVrIG9uIGthcyB2w6RnYSBrw7VyZ2UgKCpWZXJ5IEhpZ2gqKSB2w7VpIHbDpGdhIG1hZGFsICgqVmVyeSBMb3cqKS4NCg0KLSAgIFByb292aSBlcmluZXZhaWQgdsOkcnZpc2thYWxhc2lkIHR1bm51c2VsZSBpbmNvbWVfY2xhc3MuIE1pbGxpbmUgc29iaWIga8O1aWdlIHBhcmVtaW5pIGphIG1pbGxpbmUgZWkgc29iaSDDvGxkc2U/DQoNCi0gICBNaXMgdsOkcnZpc2thYWxhdCBvbiBrYXN1dGF0dWQgYWxsb2xldmFsIGrDpHJnbmV2YWwgcGlsZGlsPyFbXShpbWFnZXMvU2NyZWVuc2hvdCUyMDIwMjEtMDItMjElMjBhdCUyMDIxLjQxLjE1LnBuZykNCg0KYGBge3J9DQoNCmdncGxvdChsaW5uYWQsIGFlcyh4ID0gYmFjaGVsb3IgLCB5ID0gaGlnaF9zY2wsIGNvbG91ciA9IGluY29tZV9jbGFzcykpICsNCiAgZ2VvbV9wb2ludCgpICsgDQogIHNjYWxlX3hfY29udGludW91cyhuYW1lID0gIkhpZ2hlciBlZHVjYXRpb24gcGVyY2VudGFnZSIsIGxpbWl0cyA9IGMoMCwgMTAwKSwgbGFiZWxzID0gYygiMCUiLCAiMjUlIiwgIjUwJSIsICI3NSUiLCAiMTAwJSIpKSArDQogIHNjYWxlX2NvbG91cl9icmV3ZXIodHlwZSA9ICJxdWFsIiwgcGFsZXR0ZSA9IDMpIA0KICAjc2NhbGVfY29sb3VyX3ZpcmlkaXNfZChuYW1lID0gIlBlcmMuIGJhY2hlbG9yIikNCmBgYA0KDQojIyMgDQo=